﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MapData;
using System.IO;

namespace ExportData {
    
    /// <summary>
    /// An object which exports a MapDocument as a KML file
    /// </summary>
    public class ExportKML : ExportMapDocument {

        
        /// <summary>
        /// Pre: A valid file path and mapdocument object need to passed to the function
        /// Post: A KML file equivilant mapdocument is created and saved.
        /// </summary>
        /// <param name="file"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        public override bool Save(string file, MapData.MapDocument doc) {
            //open file
            StreamWriter outputFile = new StreamWriter(file, false);

            outputFile.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            outputFile.WriteLine("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
            outputFile.WriteLine("<Document>");
            //loop thru the objects and write the styles
            outputFile.WriteLine("  <!-- Begin Style Definitions -->");
            int count = 1;
            foreach (MapObject mapObject in doc.getMapObjects()) {
                writeStyle(mapObject, outputFile, count);
                count++;
            }            
            outputFile.WriteLine("  <!-- End Style Definitions -->");

            //loop thru the objects and write them
            foreach (MapObject mapObject in doc.getMapObjects()) {
                if (mapObject.GetType().Name == "Point") {
                    writePoint((Point)mapObject, outputFile);
                } else if (mapObject.GetType().Name == "Line") {
                    writeLine((Line)mapObject, outputFile);
                } else if (mapObject.GetType().Name == "Polygon") {
                    writePolygon((Polygon)mapObject, outputFile);
                }              
            }
            outputFile.WriteLine("</Document>");
            outputFile.WriteLine("</kml>");
            outputFile.Close();            
            return true;
        }

        private void writeStyle(MapObject item, StreamWriter outputStream, int id) {
            outputStream.WriteLine("  <Style id=\"style"+item.Style +"\">");
            if (item is Point) {
                Point aPoint = (Point)item;
                outputStream.WriteLine("    <IconStyle>");//<IconStyle id="footballerIcon ">
                outputStream.WriteLine("      <color>"+aPoint.IconColor+"</color>");//<color>ffbbbbbb</color>
                outputStream.WriteLine("      <scale>"+aPoint.IconScale+"</scale>");//<scale>1.0</scale>
                outputStream.WriteLine("      <Icon>");//<Icon>
                outputStream.WriteLine("        <href>"+aPoint.IconType+"</href>");//<href>root://icons/palette-2.png</href>
                //outputStream.WriteLine("        <x>32</x>");//<x>32</x>
                //outputStream.WriteLine("        <y>32</y>");//<y>32</y>
                //outputStream.WriteLine("        <w>32</w>");//<w>32</w>
                //outputStream.WriteLine("        <h>32</h>");//<h>32</h>
                outputStream.WriteLine("      </Icon>");//</Icon>
                outputStream.WriteLine("    </IconStyle>");//</IconStyle>
                outputStream.WriteLine("    <LabelStyle>");//* <LabelStyle id="defaultLabelStyle">
                outputStream.WriteLine("      <color>"+aPoint.LabelColor+"</color>");//<color>7fffaaff</color>
                outputStream.WriteLine("      <scale>"+aPoint.LabelScale+"</scale>");//<scale>1.5</scale>
                outputStream.WriteLine("    </LabelStyle>");//</LabelStyle>*/
                
            } else if (item is Line) {
                Line aLine = (Line)item;
                outputStream.WriteLine("    <LineStyle>");//<LineStyle id="defaultLineStyle">
                outputStream.WriteLine("      <color>" + aLine.LineColor + "</color>");//<color>ffbbbbff</color>
                outputStream.WriteLine("      <width>" + aLine.LineWidth + "</width>");//<width>15</width>
                outputStream.WriteLine("    </LineStyle>");//</LineStyle>
            } else if (item is Polygon) {
                Polygon aPoly = (Polygon)item;
                outputStream.WriteLine("    <LineStyle>");//<LineStyle id="defaultLineStyle">
                outputStream.WriteLine("      <color>"+aPoly.LineColor+"</color>");//<color>ffbbbbff</color>
                outputStream.WriteLine("      <width>"+aPoly.LineWidth+"</width>");//<width>15</width>
                outputStream.WriteLine("    </LineStyle>");//</LineStyle>
                outputStream.WriteLine("    <PolyStyle>");//<PolyStyle id="defaultPolyStyle">
                outputStream.WriteLine("      <color>"+aPoly.FillColor+"</color>");//<color>ff7fbbbb</color>
                outputStream.WriteLine("      <colorMode>normal</colorMode>");//<colorMode>normal</colorMode>
                outputStream.WriteLine("    </PolyStyle>");//</PolyStyle>
            }
            outputStream.WriteLine("  </Style>");//</Style> 
        }

        private void writeLine(Line item, StreamWriter outputStream) {            
            outputStream.WriteLine("    <Placemark>");
            outputStream.WriteLine("      <name>"+item.Name+"</name>");
            outputStream.WriteLine("      <LineString>");
            outputStream.WriteLine("        <extrude>1</extrude>");
            outputStream.WriteLine("        <tessellate>1</tessellate>");
            outputStream.WriteLine("        <altitudeMode>relativeToGround</altitudeMode>");
            outputStream.WriteLine("        <coordinates>");
            foreach (SimplePoint p in item.getPoints()) {
                outputStream.WriteLine("          " + p.ToString());
            }
            outputStream.WriteLine("        </coordinates> ");
            outputStream.WriteLine("      </LineString>");
            outputStream.WriteLine("    </Placemark>");
        }

        private void writePoint(Point item, StreamWriter outputStream) { 
            outputStream.WriteLine("    <Placemark>");
            outputStream.WriteLine("      <name>"+item.Name+"</name>");
            outputStream.WriteLine("      <Point>");
            outputStream.WriteLine("        <coordinates>");
            outputStream.WriteLine("          " + item.ToString());
            outputStream.WriteLine("        </coordinates>");
            outputStream.WriteLine("      </Point>");
            outputStream.WriteLine("    </Placemark>");
        }

        private void writePolygon(Polygon item, StreamWriter outputStream) {
            outputStream.WriteLine("    <Folder>");
            int count = 1;
            if (item.FillColor == "") {
                //PolyLine
                foreach (SimplePolygon aLine in item.getPolygons()) {
                    outputStream.WriteLine("    <Placemark>");
                    outputStream.WriteLine("      <styleUrl>#style" + item.Style + "</styleUrl>");
                    outputStream.WriteLine("      <name>" + item.Name + "</name>");
                    outputStream.WriteLine("      <description><![CDATA[\n" + item.Description + "]]></description>");
                    outputStream.WriteLine("      <LineString>");
                    outputStream.WriteLine("        <extrude>1</extrude>");
                    outputStream.WriteLine("        <tessellate>1</tessellate>");
                    outputStream.WriteLine("        <altitudeMode>relativeToGround</altitudeMode>");
                    outputStream.WriteLine("        <coordinates>");
                    foreach (SimplePoint p in aLine.getPoints()) {
                        outputStream.WriteLine("          " + p.ToString());
                    }
                    outputStream.WriteLine("        </coordinates> ");
                    outputStream.WriteLine("      </LineString>");
                    outputStream.WriteLine("    </Placemark>");
                    count++;
                }
            } else {
                //Polygon
                foreach (SimplePolygon aPoly in item.getPolygons()) {
                    outputStream.WriteLine("    <Placemark>");
                    outputStream.WriteLine("      <styleUrl>#style" + item.Style + "</styleUrl>");
                    outputStream.WriteLine("      <name>" + item.Name + "</name>");
                    outputStream.WriteLine("      <description><![CDATA[\n" + item.Description + "]]>\n</description>");
                    outputStream.WriteLine("      <Polygon id=\"" + item.Name + count + "\">");
                    outputStream.WriteLine("        <extrude>0</extrude>");
                    outputStream.WriteLine("        <tessellate>1</tessellate>");
                    //outputStream.WriteLine("        <altitudeMode>relativeToGround</altitudeMode>");
                    outputStream.WriteLine("        <outerBoundaryIs>");
                    outputStream.WriteLine("          <LinearRing id=\"OuterRing" + item.Name + count + "\">");//4
                    outputStream.WriteLine("            <coordinates>");
                    if (aPoly.getPoints().Length > 31992) {
                        SimplePoint[] points = reducePoints(aPoly.getPoints(), 31992);
                        foreach (SimplePoint p in points) {
                            outputStream.WriteLine("              " + p.ToString() + ",0");
                        }
                    } else {
                        foreach (SimplePoint p in aPoly.getPoints()) {
                            outputStream.WriteLine("              " + p.ToString() + ",0");
                        }
                    }
                    outputStream.WriteLine("            </coordinates>");
                    outputStream.WriteLine("          </LinearRing>");
                    outputStream.WriteLine("        </outerBoundaryIs>");
                    if (aPoly.hasInner()) {
                        foreach (SimplePolygon innerPoly in aPoly.getInner()) {
                            outputStream.WriteLine("        <innerBoundaryIs>");
                            outputStream.WriteLine("         <LinearRing>");
                            outputStream.WriteLine("            <coordinates>");
                            foreach (SimplePoint p in innerPoly.getPoints()) {
                                outputStream.WriteLine("              " + p.ToString() + ",0");
                            }
                            outputStream.WriteLine("            </coordinates>");
                            outputStream.WriteLine("         </LinearRing>");
                            outputStream.WriteLine("        </innerBoundaryIs>");
                        }
                    }
                    outputStream.WriteLine("      </Polygon>");
                    outputStream.WriteLine("    </Placemark>");
                    count++;
                }
            }
            outputStream.WriteLine("    </Folder>");
        }

        private SimplePoint[] reducePoints(SimplePoint[] simplePoint, int p) {
            List<SimplePoint> points;
            points = simplePoint.ToList<SimplePoint>();
            int cPoint = 1;
            //int count = 0;
            double variance = 0.00001;
            while (points.Count > p) {
                double length = Math.Abs(points[cPoint].X - points[cPoint + 1].X);
                double height = Math.Abs(points[cPoint].Y - points[cPoint + 1].Y);
                double distance = Math.Sqrt(Math.Pow(length, 2.0) + Math.Pow(height, 2.0));
                if (distance < variance) {
                    points.RemoveAt(cPoint);
                }
                cPoint++;
                if (cPoint >= (points.Count-1)) {
                    variance*=2;
                    cPoint = 1;
                }
            }
            return points.ToArray();
        }

    }
}
